package com.adcc.loadsheet.monitor.util;

import com.adcc.aoc.devLib.common.log.Log;
import com.adcc.aoc.devLib.traffic.base.ConnectState;
import com.adcc.aoc.devLib.traffic.base.channel.DataEventListener;
import com.adcc.aoc.devLib.traffic.base.channel.StateEventListener;
import com.adcc.aoc.devLib.traffic.base.packet.DataPacket;
import com.adcc.aoc.devLib.traffic.base.udp.UDPConfiguration;
import com.adcc.aoc.devLib.traffic.base.udp.UDPServer;
import com.adcc.aoc.devLib.traffic.base.udp.UDPTrafficMode;
import com.adcc.loadsheet.monitor.congifuration.AppConfiguration;
import com.adcc.loadsheet.monitor.entity.AppUploadMonitor;
import com.adcc.loadsheet.monitor.entity.MonitorStatus;
import com.adcc.loadsheet.monitor.entity.UDPConfig;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Minutes;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Map;

/**
 * AppUpload程序UDP消息工厂
 */
@Component
public class AppUploadUDPFactory extends BaseUDPFactory implements DataEventListener, StateEventListener {

    @Autowired
    private AppConfiguration appConfiguration;

    @Autowired
    private SystemConfigFactory systemConfigFactory;

    // NTP告警时间阀值
    private int ntpAlarmTime;

    // 拆分失败率告警阀值
    private double failureAlarm;

    // AppUpload监控信息
    private AppUploadMonitor monitor = new AppUploadMonitor();

    public AppUploadUDPFactory() {

    }

    public AppUploadUDPFactory(String localHost, String remoteHost, int port, UDPTrafficMode trafficMode, int readerIdle) {
        this.localHost = localHost;
        this.remoteHost = remoteHost;
        this.port = port;
        this.trafficMode = trafficMode;
        this.readerIdle = readerIdle;
    }

    @PostConstruct
    private void initialize() {
        try {
            localHost = appConfiguration.getHost();
            if(systemConfigFactory.getAppUploadConfigMap().containsKey("udp")){
                // 加载配置
                loadAppUploadConfig(systemConfigFactory.getAppUploadConfigMap());
                // 启动
                start();
            }
        } catch (Exception ex) {
            Log.error(AppUploadUDPFactory.class, "initialize() error", ex);
        }
    }

    /**
     * 获取当前的监控信息
     * @return
     */
    public AppUploadMonitor getMonitor() {
        return monitor;
    }

    /**
     * 加载appUpload配置信息
     * @param appUploadConfigMap
     */
    public void loadAppUploadConfig(Map<String, Object> appUploadConfigMap) {
        try {
            // 读取UDP配置
            if(appUploadConfigMap.containsKey("udp")) {
                UDPConfig udpConfig = (UDPConfig) appUploadConfigMap.get("udp");
                remoteHost = udpConfig.getHost();
                port = Integer.valueOf(udpConfig.getPort());
                readerIdle = Integer.valueOf(udpConfig.getTimeout());
            }

            // 读取NTP配置
            if (appUploadConfigMap.containsKey("ntp")) {
                Map<String, String> ntpMap = (Map<String, String>) appUploadConfigMap.get("ntp");
                ntpAlarmTime = ntpMap.containsKey("alarmTime") ? Integer.valueOf(ntpMap.get("alarmTime")) : 0;
            }

            // 读取报文拆分配置
            if (appUploadConfigMap.containsKey("msgSplit")) {
                Map<String, String> msgSplitMap = (Map<String, String>) appUploadConfigMap.get("msgSplit");
                failureAlarm = msgSplitMap.containsKey("failureAlarm") ? Double.valueOf(msgSplitMap.get("failureAlarm")) : 0D;
            }

            // 设置当前监控信息
            monitor.setLocalDateTime(DateTime.now(DateTimeZone.UTC).toString("yyyy-MM-dd HH:mm:ss"));

        } catch (Exception ex) {
            Log.error(AppUploadUDPFactory.class, "loadAppUploadConfig() error", ex);
        }

    }

    public void start() {
        UDPConfiguration u = new UDPConfiguration();
        u.setLocalHost(localHost);
        u.setRemoteHost(remoteHost);
        u.setPort(port);
        u.setWorkerGroup(5);
        u.setTrafficMode(trafficMode);
        u.setRcvBuf(1024*128);
        u.setReaderIdle(readerIdle);
        u.setWriterIdle(0);
        u.setAllIdle(0);
        server = new UDPServer(u);
        server.setDataEventListener(this);
        server.setStateEventListener(this);
        server.start();
    }

    public void stop() {
        if(server != null) {
            server.stop();
        }
    }

    public void restart() {
        stop();
        start();
    }

    /**
     * 错误的监控信息
     */
    private void errorStatus() {
        monitor.setNtpStatus(false);
        monitor.setLoadsheetStatus(false);
        monitor.setStatus(MonitorStatus.ABNORMAL);
        // 本地时间（NTP时间）
        monitor.setLocalDateTime(DateTime.now(DateTimeZone.UTC).toString("yyyy-MM-dd HH:mm:ss"));
        monitor.setRemoteDateTime("");
        monitor.setMinutesBetween(0);
        monitor.setFailureRate(0D);
    }

    @Override
    public void onData(DataPacket packet) {
        try {
            // 避免多个UDP使用同一个接口发送数据，造成接收数据混乱
            if(remoteHost.equals(packet.getHead().get("remoteAddress"))){
                String msg = new String(packet.getData());
                Log.info(AppUploadUDPFactory.class, "receive appUpload udp msg: " + msg);
                JSONObject jsonObject = JSON.parseObject(msg);

                // 本地时间（NTP时间）
                monitor.setLocalDateTime(DateTime.now(DateTimeZone.UTC).toString("yyyy-MM-dd HH:mm:ss"));

                // 舱单接收总数
                if(jsonObject.containsKey("Receive")){
                    monitor.setReceiveCount(jsonObject.getLong("Receive"));
                }

                // 舱单拆分总数
                if(jsonObject.containsKey("Fail")){
                    monitor.setFailCount(jsonObject.getLong("Fail"));
                }

                // 舱单报文最新接收时间
                if(jsonObject.containsKey("LastRevTime")){
                    monitor.setLastRecvTime(DateTime.parse(jsonObject.getString("LastRevTime"), DateTimeFormat.forPattern("yyyyMMddHHmmss")).toString("yyyy-MM-dd HH:mm:ss"));
                }

                // 舱单拆分失败率
                if(jsonObject.containsKey("Rate")){
                    monitor.setFailureRate(jsonObject.getDouble("Rate"));
                }

                // UDP发送时间，即远程时间（运行程序的本地时间）
                if(jsonObject.containsKey("sendTime")){
                    String timestr=jsonObject.getString("sendTime");
                    if(timestr.contains("/")){
                        timestr=DateTime.parse(timestr, DateTimeFormat.forPattern("yyyy/MM/dd HH:mm:ss")).toString("yyyy-MM-dd HH:mm:ss");
                    }
                    monitor.setRemoteDateTime(timestr);
                }

                // 进行本地时间和远程时间对比，计算时间差(远程时间-本地时间)
                int minutesBetween = Minutes.minutesBetween(DateTime.parse(monitor.getLocalDateTime(), DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")), DateTime.parse(monitor.getRemoteDateTime(), DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"))).getMinutes();
                monitor.setMinutesBetween(minutesBetween);

                // 进行NTP状态判断（依据时间差） 告警时间阀值为0时，状态为true
                monitor.setNtpStatus(ntpAlarmTime == 0 || Math.abs(monitor.getMinutesBetween()) < ntpAlarmTime);

                // 进行舱单拆分服务状态判断（依据拆分失败率） 拆分失败了阀值为0时，状态为true
                monitor.setLoadsheetStatus(failureAlarm == 0D || Math.abs(monitor.getFailureRate()) < failureAlarm);

                // 进行监控状态判断
                if(!monitor.isNtpStatus() || !monitor.isLoadsheetStatus()){
                    monitor.setStatus(MonitorStatus.ABNORMAL);
                }else{
                    monitor.setStatus(MonitorStatus.NORMAL);
                }
            }
        } catch (Exception ex) {
            errorStatus();
            Log.error(AppUploadUDPFactory.class, "initialize() error", ex);
        }
    }

    @Override
    public void onState(String channelID, String remoteAddress, ConnectState state) {
        try {
            if(state != ConnectState.CONNECTED){
                // 超时空闲或者接收异常时，所有状态都为异常
                errorStatus();
            }
        } catch (Exception ex) {
            errorStatus();
            Log.error(AppUploadUDPFactory.class, "onState() error", ex);
        }
    }

}
